/*
 * XMLTableModel.java
 *
 * Created on August 7, 2007, 8:53 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package com.afaker.rss.gui.table;

import com.afaker.rss.common.GeneralUtils;
import com.afaker.rss.context.ApplicationContext;
import com.afaker.rss.context.ResourceContext;
import com.afaker.rss.feed.biz.FeedFetcher;
import com.afaker.rss.feed.biz.ROMEFeedFetcher;
import com.afaker.rss.feed.FeedEntry;
import com.afaker.rss.feed.EntrySet;
import com.afaker.rss.feed.Feed;
import com.afaker.rss.gui.tree.RSSTreeNode;
import com.afaker.rss.gui.action.MenuAction;
import com.afaker.rss.gui.table.listener.TableDataListener;
import com.sun.syndication.fetcher.FetcherException;
import com.sun.syndication.io.FeedException;
import com.afaker.rss.xml.XMLDocumentFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.table.AbstractTableModel;
import org.jdom.Element;

/**
 *
 * @author bruce
 */
public class XMLTableModel extends AbstractTableModel {

    private FeedFetcher fetcher;
    private String uri;
    private TableDataListener listener;
    private Feed feed;
    private EntrySet entries;
    private Object obj[][];
    private Vector data;
    private ArrayList<Class> types;
    private ArrayList<String> list;
    private XMLDocumentFactory xml = new XMLDocumentFactory();
    private ApplicationContext appContext  = ApplicationContext.getApplicationContext();
    private boolean flag;
    private int i;
    //These node or attribute name below from the xml file that under the "archive" directory
    public static final String FEED_NODE_NAME = "feed";
    public static final String ENTRY_NODE_NAME = "entry";
    public static final String NUMBER_ATTRIBUTE_NAME = "number";
    public static final String TITLE_NODE_NAME = "number";
    /** Creates a new instance of XMLTableModel */
    public XMLTableModel() {
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public void addTableDataListener(TableDataListener listener) {
        this.listener = listener;
    }

    public void setDataSource(String uri, boolean isUpdate) throws FeedException, Exception {
        this.uri = uri;
        getFeed(uri);
        addEntries();
        setData(true);
        // Collections.sort(data,new FeedDataComparator())
        if (isUpdate) {
            fireTableChanged(null);
        }
    }

    public void getFeed(String uri) throws IOException,
            FeedException, FetcherException, Exception {
        fetcher = new ROMEFeedFetcher(uri);
        feed = fetcher.getFeed();
    }

    public void addEntryRow(FeedEntry entry) {
        ExtractEntryInfoTemplate template = new ExtractEntryInfoTemplate();
        Vector v = new Vector();
        Object o[] = template.extract(entry, true);
        o[types.size() - 1] = getRowCount();
        entries.addEntry(entry);
        v = convertToVector(o);
        data.insertElementAt(v, 0);
        fireTableRowsInserted(0, 0);
    }

    public EntrySet addEntries() {
        if (entries == null) {
            entries = new EntrySet();
        }
        entries.addAll((EntrySet) feed.getEntries());
        return entries;
    }

    public EntrySet getEntrySet() {
        return entries;
    }

    public String getFeedTitle() {
        return feed.getTitle();
    }

    public FeedEntry getSelectedEntry(int selected) {
        return entries.getEntry(selected);
    }

    public boolean isCellEditable(int row, int col) {
        if (col == 0) {
            return true;
        } else {
            return false;
        }
    }

    public void setValueAt(Object value, int row, int col) {
        if (!uri.startsWith("http")) {
            Boolean isMarked = (Boolean) getValueAt(row, col);
            //Because only the first column can be edited and the second column must be "Title"
            //, I can write down the following code
            String entryTitle = (String) getValueAt(row, col + 1);
            if (isMarked == null) {
                return;
            }
            String url = GeneralUtils.getLocalChannelName(
                    entries.getEntry(row).getFeedName(),
                    false, true);
            if (url != null) {
                if (!new File(url).exists()) {
                    return;
                }
                xml.build(url);
                Element root = xml.getRootElement();
                Element newEntry = null;
                Element title = null;
                int num = Integer.parseInt(root.getAttributeValue(NUMBER_ATTRIBUTE_NAME));
                if (!isMarked) {
                    newEntry = new Element(ENTRY_NODE_NAME);
                    title = new Element(TITLE_NODE_NAME);
                    title.setText(entryTitle);
                    newEntry.addContent(title);
                    root.addContent(newEntry);
                    num++;
                } else {
                    if (flag) {
                        return;
                    }
                    Iterator it = root.getChildren(ENTRY_NODE_NAME).listIterator();
                    while (it.hasNext()) {
                        newEntry = (Element) it.next();
                        title = newEntry.getChild(TITLE_NODE_NAME);
                        if (title.getText().equals(entryTitle)) {
                            root.removeContent(newEntry);
                            num--;
                            break;
                        }
                    }
                }
                root.setAttribute(NUMBER_ATTRIBUTE_NAME, num + "");
                listener.dataChanged();
                xml.outputToFile(url);
            }
        }
        Vector rowVector = (Vector) data.elementAt(row);
        rowVector.setElementAt(value, col);
        fireTableCellUpdated(row, col);
        flag = false;
    }

    public Object getValueAt(int row, int col) {
        if (obj == null) {
            return null;
        }
        if (data.size() == 0) {
            return null;
        }
        if (row >= data.size()) {
            return null;
        }
        Vector rowVector = (Vector) data.elementAt(row);
        return rowVector.elementAt(col);
    }

    public void setNullData() {
        obj = null;
        entries = null;
        data = null;
        types = null;
        list = null;
        fireTableChanged(null);
    }

    /**
     * Returns the number of rows in this data table.
     * @return the number of rows in the model
     */
    public int getRowCount() {
        if (entries == null) {
            return 0;
        }
        // return entries.size();
        return data.size();
    }

    /**
     * Returns the number of columns in this data table.
     * @return the number of columns in the model
     */
    public int getColumnCount() {
        if (list == null) {
            return 0;
        }
        return list.size();
    }

    /**
     * Returns the column name.
     *
     * @return a name for this column using the string value of the
     * appropriate member in <code>columnIdentifiers</code>.
     * If <code>columnIdentifiers</code> does not have an entry
     * for this index, returns the default
     * name provided by the superclass.
     */
    @Override
    public String getColumnName(int column) {
        if (list == null) {
            return null;
        }
        return list.get(column);
    }

    /**
     * Returns the objects' classes in the columns
     */
    @Override
    public Class getColumnClass(int columnIndex) {
        if (types == null) {
            return null;
        }
        return types.get(columnIndex);
    }

    public void showAll(Object obj) {
        if (obj instanceof Element) {
            Element root = (Element) obj;
            ArrayList feedList = MenuAction.getAllFeedElements(root);
            FeedFetcher fetcher = null;
            RSSTreeNode select;
            Vector v = new Vector();
            if (feedList == null) {
                return;
            }
            for (int i = 0; i < feedList.size(); i++) {
                select = new RSSTreeNode((Element) feedList.get(i));
                if (select.isFeed()) {
                    try {
                        getFeed(GeneralUtils.getLocalChannelName(select.getTitle(),true,true));
                        uri = GeneralUtils.getLocalChannelName(select.getTitle(),true,true);
                        addEntries();
                    } catch (FeedException ex) {
                        ex.printStackTrace();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
            if (entries == null) {
                return;
            }
            if (entries.size() == 0) {
                return;
            }
            setData(false);
            fireTableChanged(null);
        }
    }

    /**Parse the feed's entries to gain the entries information,for instance,entry's title,author,etc.*/
    public void setData(boolean isAll) {
        if (types == null) {
            types = new ArrayList<Class>();
        }
        int size = entries.size();
        final boolean isTemp = false;
        obj = new Object[size][8];
        if (list == null) {
            list = new ArrayList<String>();
        }
        list.add(appContext.getProperty("readed.col.name"));
        list.add(appContext.getProperty("title.col.name"));
        types.add(Boolean.class);
        types.add(String.class);
        ExtractEntryInfoTemplate template = new ExtractEntryInfoTemplate();
        for (i = 0; i < obj.length; i++) {
            obj[i] = template.extract(entries.getEntry(i), isAll);
            if (uri != null && uri.startsWith("http")) {
                obj[i][0] = false;
            } else {
                obj[i][0] = isMarkedNews(entries.getEntry(i), entries.getEntry(i).getFeedName(), isTemp);
            }
        }
        types.addAll(template.getTypes());
        list.addAll(template.getColumnNames());
        data = null;
        setDataVector(obj);
    }

    private void setDataVector(Object[][] obj) {
        data = convertToVector(obj);
    }

    protected static Vector convertToVector(Object[][] anArray) {
        if (anArray == null) {
            return null;
        }
        Vector v = new Vector(anArray.length);
        for (int i = 0; i < anArray.length; i++) {
            v.addElement(convertToVector(anArray[i]));
        }
        return v;
    }

    protected static Vector convertToVector(Object[] anArray) {
        if (anArray == null) {
            return null;
        }
        Vector v = new Vector(anArray.length);
        for (int i = 0; i < anArray.length; i++) {
            v.addElement(anArray[i]);
        }
        return v;
    }

    public static boolean isMarkedNews(FeedEntry entry, String fileName, boolean isTemp) {
        XMLDocumentFactory xml = new XMLDocumentFactory();
        String dir;
        if (isTemp) {
            dir = ResourceContext.getResourceContext().getProperty("temp.dir");
        } else {
            dir = ResourceContext.getResourceContext().getProperty("archive.dir");
        }
        xml.build(dir + GeneralUtils.getLocalChannelName(fileName, true, false));
        Iterator it = xml.getRootElement().getChildren(ENTRY_NODE_NAME).listIterator();
        Element f = null;
        while (it.hasNext()) {
            f = (Element) it.next();
            Element title = f.getChild(TITLE_NODE_NAME);
            if (title != null) {
                if (entry.getTitle().equals(title.getText())) {
                    return true;
                }
            }
        }
        return false;
    }
}